package com.zodiac.alioth.web.interceptor;

import com.alibaba.fastjson.JSON;
import com.zodiac.alioth.common.annotation.NotLogParams;
import com.zodiac.alioth.common.annotation.NotLogResults;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * controller日志
 *
 * @author insight
 */
@Slf4j
@Aspect
@Component
public class WebLogAspect {

    @Pointcut("execution(public * com.zodiac.alioth.web.controller..*(..))")
    public void methodLog() {
    }

    @Around("methodLog()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long time = System.currentTimeMillis() - start;
        log.info("class:{}, method:{}, spendTime:{}, param:{}, result:{}", joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName(), time,
                isNeedLogParams(joinPoint) ? render(joinPoint) : "DECRYPT",
                isNeedLogResults(joinPoint) ? JSON.toJSONString(proceed) : "DECRYPT");
        return proceed;
    }

    private String render(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (args == null) {
            return null;
        }
        List<Object> objects = new ArrayList<>();
        for (Object object : args) {
            if (object instanceof HttpServletRequest || object instanceof HttpServletResponse) {
                continue;
            }
            objects.add(object);
        }
        String str;
        try {
            str = JSON.toJSONString(objects);
        } catch (Exception e) {
            str = objects.toString();
        }
        return str;
    }

    private boolean isNeedLogParams(ProceedingJoinPoint joinPoint) {
        try {
            Signature sig = joinPoint.getSignature();
            MethodSignature mSig = (MethodSignature) sig;
            Object target = joinPoint.getTarget();
            Method currentMethod = target.getClass().getMethod(mSig.getName(), mSig.getParameterTypes());

            // 判断是否含有NotLogParams注解
            if (currentMethod.getAnnotation(NotLogParams.class) != null) {
                return false;
            }
        } catch (NoSuchMethodException e) {
            return true;
        }

        return true;
    }

    private boolean isNeedLogResults(ProceedingJoinPoint joinPoint) {
        try {
            Signature sig = joinPoint.getSignature();
            MethodSignature mSig = (MethodSignature) sig;
            Object target = joinPoint.getTarget();
            Method currentMethod = target.getClass().getMethod(mSig.getName(), mSig.getParameterTypes());

            // 判断是否含有NotLogResults注解
            if (currentMethod.getAnnotation(NotLogResults.class) != null) {
                return false;
            }
        } catch (NoSuchMethodException e) {
            return true;
        }

        return true;
    }
}
